iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
佛心分享-IT 人職涯歷練

從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始系列 第 4

Day 04|到了 React,為什麼不直接改畫面?

  • 分享至 

  • xImage
  •  

前幾天一路從:

看流程
↓
拆步驟
↓
找資料放在哪裡

走到現在,終於可以把同一個概念搬進 React。

如果以前使用原生 JavaScript,我們可能會直接建立元素:

const li = document.createElement("li");

li.textContent = text;

memoList.appendChild(li);

這種寫法很直覺:

我要讓畫面多一筆資料,所以我直接去改畫面。

但到了 React,常看到的卻是:

const [memos, setMemos] = useState<string[]>([]);

然後畫面:

{memos.map((memo, index) => (
  <li key={`${memo}-${index}`}>
    {memo}
  </li>
))}

一開始我最不懂的其實不是 useState 怎麼寫。

而是:

我明明只是想多一個 <li>,為什麼 React 不叫我直接建立它?


原生 JavaScript:直接修改 DOM

先看熟悉的版本:

addBtn.addEventListener("click", () => {
  const text = memoInput.value.trim();

  if (!text) return;

  const li = document.createElement("li");

  li.textContent = text;

  memoList.appendChild(li);
});

流程可以翻成人話:

使用者點 +
↓
取得輸入內容
↓
建立 li
↓
把文字放進 li
↓
把 li 加進畫面

這裡的思考方式比較像:

我要怎麼修改現在的 DOM?


React:先改資料,再讓畫面跟著變

到了 React,會換一個思考方式。

先準備資料:

const [memos, setMemos] = useState<string[]>([]);

先不要急著背 useState

可以先理解成:

memos
→ 現在的備忘錄資料

setMemos
→ 更新 memos 的方法

一開始:

[]

新增「買牛奶」之後:

["買牛奶"]

React 再根據這份資料決定畫面要顯示什麼。

所以流程變成:

使用者操作
↓
更新 State
↓
State 改變
↓
React 重新 Render
↓
UI 跟著改變

這裡是我覺得最重要的差異:

原生 JavaScript
→ 我直接改畫面

React
→ 我先改資料
→ React 再根據資料更新畫面

把新增功能串起來

先看完整一點的簡化範例:

import { useState } from "react";

export default function Memo() {
  const [text, setText] = useState("");
  const [memos, setMemos] = useState<string[]>([]);

  const handleAdd = () => {
    const value = text.trim();

    if (!value) return;

    setMemos(prev => [...prev, value]);
    setText("");
  };

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />

      <button onClick={handleAdd}>
        +
      </button>

      <ul>
        {memos.map((memo, index) => (
          <li key={`${memo}-${index}`}>
            {memo}
          </li>
        ))}
      </ul>
    </>
  );
}

第一次看到這段,如果又從第一行開始硬讀,很容易再次迷路。

所以還是用前幾天的方法:

使用者輸入「買牛奶」
↓
onChange
↓
setText()
↓
text 更新

使用者按下 +
↓
onClick
↓
handleAdd()
↓
取得 text
↓
setMemos()
↓
memos 更新
↓
React Render
↓
畫面出現「買牛奶」

這樣整段就開始有一條線可以追。


onClick 是誰在負責點擊?

以前可能寫:

addBtn.addEventListener("click", handleAdd);

到了 React:

<button onClick={handleAdd}>
  +
</button>

可以先簡單對照:

JavaScript

addEventListener("click", handleAdd)

↓

React

onClick={handleAdd}

同樣地:

<input
  onChange={e => setText(e.target.value)}
/>

就是:

輸入內容改變時,執行這段處理。

所以現在先記:

點擊
→ onClick

輸入改變
→ onChange

這裡還不需要把 useEffect 拉進來。

因為一般的按鈕點擊,不是靠 useEffect 處理。


setMemos() 又在做什麼?

這一行:

setMemos(prev => [...prev, value]);

可以拆開看。

假設目前:

prev = ["買牛奶"];

這次新增:

value = "寫文章";

這段:

[...prev, value]

會變成:

["買牛奶", "寫文章"]

所以:

setMemos(prev => [...prev, value]);

可以先理解成:

把原本的資料保留下來,再加入新的資料,最後更新 memos。

流程:

舊資料
["買牛奶"]

↓ 加入新資料

["買牛奶", "寫文章"]

↓ setMemos()

State 更新

map() 是怎麼讓資料變成畫面的?

假設:

memos = ["買牛奶", "寫文章"];

畫面需要的是:

<li>買牛奶</li>
<li>寫文章</li>

所以:

memos.map((memo, index) => (
  <li key={`${memo}-${index}`}>
    {memo}
  </li>
))

可以理解成:

["買牛奶", "寫文章"]
↓
一筆一筆轉換

買牛奶
→ <li>買牛奶</li>

寫文章
→ <li>寫文章</li>

所以在這裡,map() 不只是「跑迴圈」。

更像是:

把 Array 裡的資料,轉換成 React 要 Render 的內容。


關於 key

這個範例裡:

key={`${memo}-${index}`}

只是為了讓教學先聚焦在 State → Render

真實專案如果資料本身有穩定且唯一的 ID,會優先使用:

<li key={memo.id}>

而不是依賴 index。

這個之後遇到列表問題時再單獨拆。


真實專案裡,本質還是同一條線

到了實際的 React 專案,畫面可能不是備忘錄,而是一個 Table:

API
↓
Response
↓
State
↓
Table dataSource
↓
React Render
↓
畫面

所以如果畫面資料不對,我現在會開始往回找:

這個 Table 現在吃哪份資料?
↓
那份資料是哪個 State?
↓
State 是誰更新的?
↓
是 Props 傳進來的?
還是 API Response?

這其實還是前幾天一直在做的同一件事:

追資料從哪裡來,又往哪裡去。


今天真正要記的不是 useState 格式

如果只背:

const [memos, setMemos] = useState([]);

很容易過幾天又忘記。

但如果記住:

State
↓
畫面依賴的資料

使用者操作
↓
事件發生
↓
更新 State
↓
React Render
↓
UI 跟著資料變化

之後再看到:

useState
setMemos
onClick
onChange
map

它們就不再是五個分開的咒語。

而是同一條流程裡,不同位置的工具。

下一篇就接一個 React 裡非常常見,也很容易混亂的問題:

State 跟 Props 到底差在哪?這份資料到底是誰的?


上一篇
Day 03|資料到底住在哪?為什麼重新整理後有的消失、有的還在?
下一篇
Day 05|State 跟 Props 到底差在哪?這份資料到底是誰的?
系列文
從 UI/UX 麻瓜到工程魔法師|從讀懂程式開始9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言